﻿//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using MainDev.RemoteASM.Handlers;
using Styx;
using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.CommonBot.Frames;
using Styx.Helpers;
using Styx.Patchables;
using Styx.Pathing;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.WoWInternals.World;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Mists_of_Pandaria
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Mists_of_Pandaria
#endif
{
    internal class GateOfTheSettingSunHeroic : Dungeon
    {
        #region Overrides of Dungeon

        private const float ElevatorBottomZ = 388.1946f;
        private const float ElevatorTopZ = 430.2219f;
        private readonly WoWPoint _elevatorBottomBoardLoc = new WoWPoint(1195.256, 2301.015, 388.8313);
        //private readonly WoWPoint _elevatorBottomWaitLoc = new WoWPoint(1192.767, 2296.851, 388.1232);
        private readonly WoWPoint _elevatorTopBoardLoc = new WoWPoint(1194.88, 2301.346, 430.8586);
        //private readonly WoWPoint _elevatorTopWaitLoc = new WoWPoint(1199.783, 2296.642, 430.8624);
        private readonly WoWPoint _gadokRoomLoc = new WoWPoint(1195.002, 2305.316, 430.8587);
        private readonly WoWPoint _elevatorBottomWaitLoc = new WoWPoint(1203.317, 2304.725, 388.1101);
        private readonly WoWPoint _elevatorTopWaitLoc = new WoWPoint(1203.566, 2304.513, 430.8623);

        public override uint DungeonId
        {
            get { return 471; }  

        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(698.5774, 2079.577, 374.6915); }
        }

        public override void OnEnter()
        {
            _wallScalingPackTimer = null;
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret.ToUnit();
                    if (unit != null)
                    {
                        if (_inCombatMobs.Contains(unit.Entry) && unit.Combat && Me.Combat && !unit.IsTargetingMeOrPet && !unit.IsTargetingMyPartyMember && !unit.TaggedByMe)
                            return true;

                        // ignore Striker Gadok if he's on a strafing run  (unless you're healer since some healer routines won't heal if theres nothing it Targeting)
                        if (unit.Entry == StrikerGadokId && unit.Z > StrikerGadokStrafeZ && !Me.IsHealer())
                            return true;

                        if (unit.Entry == KrikthikSwarmerId && (!Me.IsTank() || !_pickupSwarmers))
                            return true;

                        if (unit.Entry == RaigonnId && unit.Combat && unit.HasAura("Impervious Carapace") && Me.IsDps())
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == RaigonnId && !unit.Combat && Me.IsTank() && unit.Distance < 40)
                        outgoingunits.Add(unit);
                    if (unit.Entry == WeakSpotId && Me.HasAura(FactionOverrideId))
                        outgoingunits.Add(unit);
                }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == WeakSpotId)
                        priority.Score += 5000;

                    // ignore the adds on commander Rimok if I'm dps.
                    if (unit.Entry == CommanderRimokId && Me.IsDps())
                        priority.Score += 5000;

                    if (unit.Entry == CommanderRimokId && Me.IsTank() && unit.CurrentTargetGuid != Me.Guid)
                        priority.Score += 50000;
                }
            }
        }

        public override MoveResult MoveTo(WoWPoint location)
        {
            if (ElevatorBehavior(location))
                return MoveResult.Moved;

            var myLoc = Me.Location;
            // are we moving to last boss?
            if (myLoc.Z > 350 && location.Z < 350)
            {
                if (myLoc.Distance(_ropeLoc) > 4)
                    return Navigator.MoveTo(_ropeLoc);
                if (myLoc.DistanceSqr(_ropeLoc) > 2)
                    Navigator.PlayerMover.MoveTowards(_ropeLoc);
                if (Me.Mounted)
                    Mount.Dismount("Using Artillery");
                var rope = ObjectManager.GetObjectsOfType<WoWUnit>().OrderBy(u => u.DistanceSqr).FirstOrDefault(u => u.Entry == RopeId);
                if (rope != null)
                    rope.Interact();
                return MoveResult.Moved;
            }
            // are we at last boss and we want to move to the walls?
            if (myLoc.Z < 350 && location.Z > 350)
            {
                if (myLoc.Distance(_artilleryLoc) > 4)
                    return Navigator.MoveTo(_artilleryLoc);
                if (Me.Mounted)
                    Mount.Dismount("Using Artillery");
                var artillery = ObjectManager.GetObjectsOfType<WoWUnit>().OrderBy(u => u.DistanceSqr).FirstOrDefault(u => u.Entry == ArtilleryId);
                if (artillery != null)
                    artillery.Interact();
                return MoveResult.Moved;
            }
            return MoveResult.Failed;
        }

        #endregion

        private const uint KrikthikInfiltratorId = 56890;
        private const uint KrikthikInfiltrator2Id = 58108;
        private const uint KrikthikWindShaperId = 59801;
        private const uint KrikthikRagerId = 59800;
        private const uint VolatileMunitionsId = 56896;
        private const uint StableMunitionsId = 56917;
        private const uint LeverId = 211284;
        private const int NorthSouthStrafingRunId = 107342;
        private const int EastWeastStrafingRunId = 107284;
        private const uint StrikerGadokId = 56589;
        private const float StrikerGadokStrafeZ = 432;
        private const uint KrikthikBombardierId = 56706;
        private const int FrenziedAssaultId = 107120;
        private const uint CommanderRimokId = 56636;
        private const uint KrikthikSwarmerId = 59835;
        private const uint RopeId = 64710;
        private const uint ArtilleryId = 66904;
        private const int ImperviousCarapaceId = 107118;
        private const uint RaigonnId = 56877;
        private const uint WeakSpotId = 56895;
        private const int FactionOverrideId = 107132;

        private const uint ElevatorId = 211013;
        private readonly WoWPoint _artilleryLoc = new WoWPoint(866.2046, 2225.777, 311.2762);
        private readonly WoWPoint _eastPoint = new WoWPoint(1195.388, 2275.083, 430.8744);
        private readonly uint[] _inCombatMobs = new[] { KrikthikInfiltratorId, KrikthikInfiltrator2Id, KrikthikWindShaperId, KrikthikRagerId };

        private readonly uint[] _mantidMunitionsIds = new uint[] { 56911, 56918, 56919, 56920, 59205, 59206, 59207, 59208 };

        private readonly WoWPoint _northPoint = new WoWPoint(1223.854, 2304.638, 430.874);
        private readonly WoWPoint _ropeLoc = new WoWPoint(842.3488, 2299.293, 381.3212);
        private readonly WoWPoint _southPoint = new WoWPoint(1166.775, 2304.836, 430.874);
        private readonly WoWPoint _westPoint = new WoWPoint(1194.746, 2333.279, 430.874);
        private bool _pickupSwarmers;

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(64467, "Bowmistress Li", Mode = CallBehaviorMode.Proximity)]
        public Composite BowmistressLiEncounter()
        {
            return new PrioritySelector(
                new Decorator<WoWUnit>(u => u.QuestGiverStatus == QuestGiverStatus.Available, ScriptHelpers.CreatePickupQuest(64467)),
                new Decorator<WoWUnit>(u => u.QuestGiverStatus == QuestGiverStatus.TurnIn, ScriptHelpers.CreateTurninQuest(64467)));
        }

        [EncounterHandler(0)]
        public Composite RootEncounter()
        {
            return new PrioritySelector(
                // run from the Munitations explosions.
                ScriptHelpers.CreateRunAwayFromBad(
                    ctx => true,
                    3,
                    u => _mantidMunitionsIds.Contains(u.Entry),
                    u =>
                    {
                        var start = u.Location;
                        return Me.Location.GetNearestPointOnSegment(start, start.RayCast(WoWMathHelper.NormalizeRadian(u.Rotation), 20));
                    }),
                ScriptHelpers.CreateRunAwayFromBad(ctx => true, 5, VolatileMunitionsId),
                ScriptHelpers.CreateRunAwayFromBad(ctx => Me.TransportGuid == 0, 2f, LeverId),
                CreateFollowerElevatorBehavior());
        }

        private Composite CreateFollowerElevatorBehavior()
        {
            return new Decorator(
                ctx => !StyxWoW.Me.IsTank(),
                new Action(
                    ctx =>
                    {
                        var tankI = StyxWoW.Me.GroupInfo.RaidMembers.FirstOrDefault(p => p.HasRole(WoWPartyMember.GroupRole.Tank));

                        if (tankI != null)
                        {
                            var tank = tankI.ToPlayer();
                            var myFloorLevel = FloorLevel(Me.Location);
                            var tankLoc = tank != null ? tank.Location : tankI.Location3D;
                            var tankLevel = FloorLevel(tankLoc);
                            var elevatorRestingZ = myFloorLevel == 1 ? ElevatorBottomZ : ElevatorTopZ;
                            var elevatorBoardLoc = myFloorLevel == 2 ? _elevatorTopBoardLoc : _elevatorBottomBoardLoc;
                            var elevatorWaitLoc = myFloorLevel == 2 ? _elevatorTopWaitLoc : _elevatorBottomWaitLoc;

                            // do we need to get on a lift?
                            if (IsOnLift(tank) && !IsOnLift(Me) && tankLevel == myFloorLevel)
                            {
                                var ele = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(g => g.Entry == ElevatorId);
                                bool elevatorIsReadyToBoard = ele != null && Math.Abs(ele.Z - elevatorRestingZ) <= 0.5f;
                                if (elevatorIsReadyToBoard)
                                {
                                    if (Me.Location.DistanceSqr(elevatorBoardLoc) > 3 * 3)
                                    {
                                        Logger.Write("[Elevator Manager] Boarding Elevator");
                                        Navigator.PlayerMover.MoveTowards(elevatorBoardLoc);
                                    }
                                    else
                                    {
                                        Logger.Write("[Elevator Manager] Jumping");
                                        WoWMovement.Move(WoWMovement.MovementDirection.JumpAscend);
                                        WoWMovement.MoveStop(WoWMovement.MovementDirection.JumpAscend);
                                    }
                                }
                                return RunStatus.Success;
                            }

                            // do we need to get off lift?
                            if (!IsOnLift(tank) && IsOnLift(Me) && tankLevel == myFloorLevel)
                            {
                                var ele = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(g => g.Entry == ElevatorId);
                                bool elevatorIsReadyToBoard = ele != null && Math.Abs(ele.Z - elevatorRestingZ) <= 0.5f;
                                if (!elevatorIsReadyToBoard)
                                    return RunStatus.Success;
                            }

                        }
                        return RunStatus.Failure;
                    }));
        }

        public bool ElevatorBehavior(WoWPoint destination)
        {
            if (Avoidance.AvoidanceManager.IsRunningOutOfAvoid)
                return false;
            var myloc = StyxWoW.Me.Location;

            var myFloorLevel = FloorLevel(myloc);
            var destinationFloorLevel = FloorLevel(destination);

            if (myFloorLevel != destinationFloorLevel)
            {
                var ele = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(g => g.Entry == ElevatorId);

                var elevatorRestingZ = myFloorLevel == 1 ? ElevatorBottomZ : ElevatorTopZ;
                var elevatorBoardLoc = destinationFloorLevel == 1 ? _elevatorTopBoardLoc : _elevatorBottomBoardLoc;
                var elevatorWaitLoc = destinationFloorLevel == 1 ? _elevatorTopWaitLoc : _elevatorBottomWaitLoc;
                var lever = ObjectManager.GetObjectsOfType<WoWGameObject>().Where(o => o.Entry == 211284).OrderBy(o => o.DistanceSqr).FirstOrDefault();
                bool elevatorIsReadyToBoard = ele != null && Math.Abs(ele.Z - elevatorRestingZ) <= 0.5f;
                // move to the lever loc
                if ((ele == null || myloc.DistanceSqr(elevatorBoardLoc) > 20 * 20 || !elevatorIsReadyToBoard && myloc.DistanceSqr(elevatorWaitLoc) > 4 * 4) && Me.TransportGuid == 0)
                {
                    Logger.Write("[Elevator Manager] Moving To Elevator");
                    var moveResult = Navigator.MoveTo(elevatorWaitLoc);
                    return moveResult != MoveResult.Failed && moveResult != MoveResult.PathGenerationFailed;
                }
                // use lever to bring elevator to our floor level.
                if (!elevatorIsReadyToBoard && myloc.DistanceSqr(elevatorWaitLoc) <= 20 * 20 && Me.TransportGuid == 0 && lever != null && lever.CanUse())
                {
                    if (myloc.DistanceSqr(elevatorWaitLoc) > 4 * 4)
                    {
                        Navigator.MoveTo(ele.Location);
                        return true;
                    }
                    Logger.Write("[Elevator Manager] Bringing Elevator to my level");
                    lever.Interact();
                }
                // get onboard of elevator.
                if (elevatorIsReadyToBoard && myloc.DistanceSqr(elevatorBoardLoc) > 3 * 3)
                {
                    if (lever != null && lever.Distance2DSqr <= 2 * 2)
                        return false;
                    Logger.Write("[Elevator Manager] Boarding Elevator");
                    // avoid getting stuck on lever
                    Navigator.PlayerMover.MoveTowards(elevatorBoardLoc);
                }
                else if (elevatorIsReadyToBoard && myloc.DistanceSqr(elevatorBoardLoc) <= 3 * 3 && Me.TransportGuid == 0)
                {
                    Logger.Write("[Elevator Manager] Jumping");
                    WoWMovement.Move(WoWMovement.MovementDirection.JumpAscend);
                    WoWMovement.MoveStop(WoWMovement.MovementDirection.JumpAscend);
                }
                if (elevatorIsReadyToBoard && Me.RaidMembers.Where(r => FloorLevel(r.Location) == myFloorLevel).All(r => r.TransportGuid == ele.Guid) && lever != null && lever.CanUse())
                {
                    Logger.Write("[Elevator Manager] Using Lever");
                    lever.Interact();
                }
                return true;
            }

            // exit elevator
            var transport = Me.TransportGuid > 0 ? Me.Transport : null;
            if (transport != null && transport.Entry == ElevatorId)
            {
                var elevatorExitZ = destinationFloorLevel == 1 ? ElevatorBottomZ : ElevatorTopZ;
                var elevatorExitLoc = destinationFloorLevel == 1 ? _elevatorBottomWaitLoc : _elevatorTopWaitLoc;
                bool elevatorIsReadyToExit = Math.Abs(transport.Z - elevatorExitZ) <= 0.5f;
                if (!elevatorIsReadyToExit || myFloorLevel != destinationFloorLevel)
                {
                    return true;
                }
            }

            return false;
        }


        private bool IsOnLift(WoWPlayer player)
        {
            return player != null && player.TransportGuid > 0 && player.Transport.Entry == ElevatorId;
        }

        private int FloorLevel(WoWPoint loc)
        {
            return loc.Z > 425 && loc.DistanceSqr(_gadokRoomLoc) <= 75 * 75 ? 2 : 1;
        }



        [EncounterHandler(56906, "Saboteur Kip'tilak")]
        public Composite SaboteurKiptilakEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                // get foot inside room before door closes
                new Decorator(ctx => Me.Y < 2277, new Action(ctx => Navigator.MoveTo(boss.Location))),
                // run away from any stable munitions that are aligned north/south or east/west with a group member with Sabotage debuf.
                ScriptHelpers.CreateRunAwayFromBad(
                    ctx => Me.RaidMembers.Any(u => u.HasAura("Sabotage")),
                    8,
                    u => u.Entry == StableMunitionsId && Me.RaidMembers.Any(p => p.HasAura("Sabotage") && (Math.Abs(u.X - p.X) < 4 || Math.Abs(u.Y - p.Y) < 4) && u.Location.Distance(p.Location) < 20)),
                ScriptHelpers.CreateRunAwayFromBad(ctx => true, 8, u => u is WoWPlayer && u != Me && u.ToPlayer().HasAura("Sabotage")),
                ScriptHelpers.CreateRunAwayFromBad(ctx => Me.HasAura("Sabotage"), 8, u => u is WoWPlayer && u != Me));
        }

        WaitTimer _wallScalingPackTimer = new WaitTimer(TimeSpan.FromSeconds(10));

        [LocationHandler(830.1323f, 2312.181f, 381.2352f, 20, "Wait For wall scaling pack")]
        public Composite WaitForWallScalingPack()
        {
            return new PrioritySelector(
                new Decorator(ctx => _wallScalingPackTimer == null,
                    new Action(ctx =>
                    {
                        _wallScalingPackTimer = new WaitTimer(TimeSpan.FromSeconds(10));
                        _wallScalingPackTimer.Reset();
                    })),
            
               new Decorator(ctx => !_wallScalingPackTimer.IsFinished && Me.IsTank() && Targeting.Instance.IsEmpty(),
                    new PrioritySelector(
                        new Decorator(ctx => Me.IsMoving,
                            new Action(ctx => WoWMovement.MoveStop())),
                        new ActionAlwaysSucceed()))
                );
        }

        [EncounterHandler(56589, "Striker Ga'dok", BossRange = 300)]
        public Composite StrikerGadokEncounter()
        {
            WoWPoint nearestAvoidStrafePoint = WoWPoint.Zero;

            WoWUnit boss = null;
            const uint acidBombId = 59813;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                // strafe run.
                new Decorator(
                    ctx => boss.Z > StrikerGadokStrafeZ && (Me.IsTank() && Targeting.Instance.TargetList.All(t => t.IsTargetingMeOrPet) || !Me.IsTank()),
                    new PrioritySelector(
                        ctx => nearestAvoidStrafePoint = GetNearestAvoidStrafePoint(boss),
                        new Decorator(ctx => Me.Location.Distance(nearestAvoidStrafePoint) > 10,
                            new Action(ctx => Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(nearestAvoidStrafePoint)))),
                // don't go anywhere.. 
                        new Decorator(ctx => Targeting.Instance.IsEmpty() && Me.IsTank(), new ActionAlwaysSucceed()))),
                // dodge the acid bombs.

                ScriptHelpers.CreateRunAwayFromBad(ctx => true, 5, acidBombId),
                ScriptHelpers.CreateRunAwayFromLocation(ctx => true, 5, m => ((WoWMissile)m).ImpactPosition, () => WoWMissile.InFlightMissiles.Where(m => m.SpellId == acidBombId)));
        }

        private WoWPoint GetNearestAvoidStrafePoint(WoWUnit gadok)
        {
            var tank = ScriptHelpers.Tank;
            var myLoc = tank != null && tank != Me ? tank.Location : Me.Location;
            var bossLoc = gadok.Location;

            var bossToNorthSouthLineDistanceSqr = bossLoc.GetNearestPointOnLine(_northPoint, _southPoint).DistanceSqr(bossLoc);
            var bossToEastWestLineDistanceSqr = bossLoc.GetNearestPointOnLine(_eastPoint, _westPoint).DistanceSqr(bossLoc);

            if (bossToEastWestLineDistanceSqr < bossToNorthSouthLineDistanceSqr)
            {
                TreeRoot.StatusText = "Avoiding East/West Strafing run";
                return myLoc.DistanceSqr(_northPoint) < myLoc.DistanceSqr(_southPoint) ? _northPoint : _southPoint;
            }
            TreeRoot.StatusText = "Avoiding North/South Strafing run";
            return myLoc.DistanceSqr(_eastPoint) < myLoc.DistanceSqr(_westPoint) ? _eastPoint : _westPoint;
        }

        [EncounterHandler(60415, "Flak Cannon", Mode = CallBehaviorMode.Proximity, BossRange = 100)]
        public Composite FlakCannonEncounter()
        {
            WoWUnit boss = null;
            const int lootSparkleId = 92406;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                new Decorator(
                    ctx => boss.HasAura(lootSparkleId) && ObjectManager.GetObjectsOfType<WoWUnit>().Any(u => u.Entry == KrikthikBombardierId && u.IsAlive),
                    ScriptHelpers.CreateInteractWithObject(() => boss)));
        }

        [ObjectHandler(211129, "Signal Flame", ObjectRange = 250)]
        public Composite SignalFlameHandler()
        {
            WoWGameObject signal = null;
            return new PrioritySelector(
                ctx => signal = ctx as WoWGameObject,

                new Decorator(ctx => signal.CanUse() && signal.Distance >= 50,
                    new Action(ctx => ScriptHelpers.MoveTankTo(signal.Location))),

                new Decorator(ctx => signal.Distance < 50 && signal.CanUse() && !ScriptHelpers.IsBossAlive("Striker Ga'dok"), ScriptHelpers.CreateInteractWithObject(() => signal)));
        }

        [EncounterHandler(56636, "Commander Ri'mok")]
        public Composite CommanderRimokEncounter()
        {
            const uint viscosFluidStalkerId = 56883;
            const int frenziedAssaultId = 107120;
            var kiteCenterLoc = new WoWPoint(1297.094, 2301.952, 388.9373);
            WoWUnit boss = null;
            WoWPoint tankStepOutOfMeleeRangePoint = WoWPoint.Zero;

            ScriptHelpers.CreateRunAwayFromBad(ctx => StyxWoW.Me.IsTank() && !_pickupSwarmers && boss != null && boss.IsValid && boss.CurrentTargetGuid == Me.Guid,
                () => kiteCenterLoc, 46, 17,
                u => u.Entry == viscosFluidStalkerId && boss != null && boss.IsValid && boss.Location.DistanceSqr(u.Location) < 4 && boss.HasAura("Viscous Fluid") && boss.Auras["Viscous Fluid"].StackCount >= 3);

            ScriptHelpers.CreateRunAwayFromBad(ctx => Me.IsDps() && !(boss.IsSafelyFacing(Me) && boss.Distance < 12), 7, viscosFluidStalkerId);

            return new PrioritySelector(
                ctx =>
                {
                    _pickupSwarmers = ObjectManager.GetObjectsOfType<WoWUnit>().Count(u => u.Combat && u != boss && u.IsTargetingMyPartyMember) >= 5;
                    return boss = ctx as WoWUnit;
                },

                new Decorator(ctx => Me.IsTank() && boss.CastingSpellId == frenziedAssaultId,
                    new PrioritySelector(
                // back away from boss so he kills the adds with his frontal attack.
                        new Decorator(ctx => (tankStepOutOfMeleeRangePoint = GetPointInFrontOfUnitAndOusideMeleeRange(boss, 1)) != WoWPoint.Zero,
                            new PrioritySelector(
                                new Decorator(ctx => Me.Location.Distance(tankStepOutOfMeleeRangePoint) <= Navigator.PathPrecision,
                                    new Action(ctx => Navigator.PlayerMover.MoveTowards(tankStepOutOfMeleeRangePoint))),

                                new Decorator(ctx => Me.Location.Distance(tankStepOutOfMeleeRangePoint) > Navigator.PathPrecision,
                                    new Action(ctx => Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(tankStepOutOfMeleeRangePoint))))
                                )),
                // bot can't move to the front we just sidestep.
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => tankStepOutOfMeleeRangePoint == WoWPoint.Zero && boss.Distance < 12, () => boss, new ScriptHelpers.AngleSpan(0, 180))
                            )),

                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => !Me.IsTank() && boss.Distance < 12, () => boss, new ScriptHelpers.AngleSpan(0, 180)));
        }

        WoWPoint GetPointInFrontOfUnitAndOusideMeleeRange(WoWUnit unit, float distancePastMeleeRange)
        {
            var facing = WoWMathHelper.NormalizeRadian(unit.Rotation);
            var point = unit.Location.RayCast(facing, 12 + distancePastMeleeRange);
            // set the Z coord to mesh level.
            var zList = Navigator.FindHeights(point.X, point.Y);
            if (zList != null && zList.Count > 0)
            {
                point.Z = zList.OrderBy(z => Math.Abs(z - point.Z)).FirstOrDefault();
                if (Navigator.CanNavigateFully(Me.Location, point))
                    return point;
            }
            return WoWPoint.Zero;
        }


        [EncounterHandler(56877, "Raigonn", BossRange = 200)]
        public Composite RaigonnEncounter()
        {
            WoWUnit boss = null;
            WoWUnit artillery = null;
            const uint artilleryId = 59819;
            const uint engulfingWindsId = 56928;
            const int vulnerabilityId = 111682;

            var roomCenterLoc = new WoWPoint(956.2532, 2275.753, 296.1056);
            var gateLoc = new WoWPoint(959.0933, 2227.044, 296.1056);

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateRunAwayFromBad(ctx => Me.HasAura("Fixate"), () => roomCenterLoc, 80, 40),
                ScriptHelpers.CreateRunAwayFromBad(ctx => true, 8, engulfingWindsId),
                new Decorator(
                    ctx =>
                    boss.HasAura("Impervious Carapace") && Me.IsDps() && Me.TransportGuid != boss.Guid && Me.PartyMembers.Count(p => p.TransportGuid == boss.Guid) < 2 &&
                    (boss.Location.DistanceSqr(gateLoc) <= 50 * 50 || Targeting.Instance.IsEmpty()) &&
                    ObjectManager.GetObjectsOfType<WoWUnit>().Any(u => u.Entry == WeakSpotId && u.HasAura(vulnerabilityId)),
                    new PrioritySelector(
                        ctx =>
                        artillery =
                        ObjectManager.GetObjectsOfType<WoWUnit>().OrderBy(u => u.DistanceSqr).FirstOrDefault(
                            u => u.Entry == artilleryId && !ObjectManager.GetObjectsOfType<WoWUnit>().Any(w => w.Entry == engulfingWindsId && u.Location.DistanceSqr(w.Location) < 12 * 12)),
                        new Decorator(ctx => Me.Mounted,
                            new Action(ctx => Mount.Dismount())),

                        new Decorator(ctx => artillery != null, ScriptHelpers.CreateInteractWithObject(() => artillery, 0, true)))),
                // if stuck in an artillery then exit 
                new Decorator(ctx => Me.TransportGuid > 0 && Me.TransportGuid > 0 && Me.Transport.Entry == artilleryId, new Action(ctx => Lua.DoString("VehicleExit()"))),
                // make sure we're facing the weak spot.
                new Decorator(ctx => Me.CurrentTargetGuid != 0 && Me.CurrentTarget.Entry == WeakSpotId && !Me.IsSafelyFacing(Me.CurrentTarget), new Action(ctx =>
                                                                                                                                                               {
                                                                                                                                                                   Me.CurrentTarget.Face();
                                                                                                                                                                   return RunStatus.Failure;
                                                                                                                                                               })),
                ScriptHelpers.CreateDispellParty("Screeching Swarm", ScriptHelpers.PartyDispellType.Magic));
        }
    }
}